package com.demo.repository.interceptor;

import com.demo.repository.annotation.SwitchDb;
import com.demo.repository.config.DynamicDataSourceHolder;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Properties;

/**
 * @author Administrator
 */
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
})
public class DynamicDataSourceInterceptor implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            //事务
            boolean transactionActive = TransactionSynchronizationManager.isActualTransactionActive();
            //默认读库
            String lookup = "db1";
            Object[] args = invocation.getArgs();
            MappedStatement ms = (MappedStatement) args[0];
            if (!transactionActive) {
                String id = ms.getId();
                Method method = getMethod(ms);
                SwitchDb switchDb = getDbType(method);
                if (Objects.isNull(switchDb)) {
                    throw new RuntimeException("未指定切库标识");
                }
                String val = switchDb.value();
                if (StringUtils.isBlank(val)) {

                }
                lookup = val;
            }else {
                //事物性走主库
            }

            DynamicDataSourceHolder.setDB(lookup);
            return invocation.proceed();
        }finally {
            DynamicDataSourceHolder.clear();
        }
    }

    private SwitchDb getDbType(Method method) {
        SwitchDb annotation = method.getDeclaredAnnotation(SwitchDb.class);
        if (Objects.isNull(annotation)) {
            annotation = method.getClass().getDeclaredAnnotation(SwitchDb.class);
        }
        return annotation;
    }

    private Method getMethod(MappedStatement ms) {
        String id = ms.getId();
        String clazz = id.replaceAll("\\.\\w+$", "");
        try {
            Class<?> cls = ClassUtils.forName(clazz, DynamicDataSourceInterceptor.class.getClassLoader());
            String methodName = id.substring(id.lastIndexOf(".")+1);
            return getUniqueMethod(cls, methodName);
        } catch (Exception e) {
        }
        return null;
    }

    private Method getUniqueMethod(Class<?> cls, String methodName) {
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
              return method;
            }
        }
        return null;
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

